package com.shanzmoo.core.service.impl;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.EnumUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.shanzmoo.base.bean.SResponse;
import com.shanzmoo.base.constant.Constant;
import com.shanzmoo.base.constant.PayStatusConstant;
import com.shanzmoo.base.constant.e.PayTypeEnum;
import com.shanzmoo.base.constant.e.WxTradeTypeEnum;
import com.shanzmoo.base.util.BillNoUtils;
import com.shanzmoo.base.util.MoneyUtils;
import com.shanzmoo.core.config.pay.MyWxPayConfig;
import com.shanzmoo.core.config.pay.PayServiceConfig;
import com.shanzmoo.core.domain.pay.*;
import com.shanzmoo.core.manager.OutletOrderManager;
import com.shanzmoo.core.service.ITradeService;
import com.shanzmoo.core.util.SWxPayUtils;
import com.shanzmoo.db.order.entity.OutletOrderDetailEntity;
import com.shanzmoo.db.order.entity.OutletOrderEntity;
import com.shanzmoo.db.order.entity.bo.PayMoneyResultBO;
import com.shanzmoo.db.order.service.IOutletOrderDetailService;
import com.shanzmoo.db.trade.entity.PayOrderBodyEntity;
import com.shanzmoo.db.trade.entity.PayOrderEntity;
import com.shanzmoo.core.domain.pay.PayResultResp;
import com.shanzmoo.core.domain.pay.WxWapPayResp;
import com.shanzmoo.core.pay.sdk.wxpay.WXPay;
import com.shanzmoo.db.trade.service.IPayOrderBodyService;
import com.shanzmoo.db.trade.service.IPayOrderCallbackService;
import com.shanzmoo.db.trade.service.IPayOrderService;
import com.shanzmoo.core.service.IWxSpecialPayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * 交接服务实现类
 *
 * @author Gzhao 2021/1/2
 */
@Slf4j
@Service
public class TradeServiceImpl implements ITradeService {

    @Autowired
    private PayServiceConfig payServiceConfig;

    @Autowired
    private IPayOrderService payOrderService;

    @Autowired
    private IPayOrderBodyService payOrderBodyService;

    @Autowired
    private IWxSpecialPayService wxSpecialPayService;

    @Autowired
    private IPayOrderCallbackService payOrderCallbackService;

    @Autowired
    private OutletOrderManager outletOrderManager;

    @Autowired
    private IOutletOrderDetailService outletOrderDetailService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SResponse<String> genQrCode(PayOrderQrCodeReq request) {
        //支付订单
        PayOrderEntity payOrder = producePayOrderInfo(request);
        PayOrderEntityLocalUtil.setPayOrder(payOrder, PayOrderBodyEntity.create(payOrder.getPayNo()));

        SResponse<String> re = payServiceConfig.getPayService(request.getPayType()).tradeQrCode();

        if (re.isOk()) {
            payOrderService.saveOrder(PayOrderEntityLocalUtil.getPayOrderEntity(), PayOrderEntityLocalUtil.getPayOrderBodyEntity());
            return SResponse.ok(re.getData());
        }
        return SResponse.fail(re.getErrno(), re.getErrmsg());

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SResponse<Boolean> tradeByQrCode(PayOrderTradeReq request) {
        //支付订单
        PayOrderEntityLocalUtil.initOrder(request.convertToPayOrderEntity());

        // 支付请求
        SResponse<PayMoneyResultBO> re = payServiceConfig.getPayService(request.getPayType())
                .tradePay(request.getAuthCode());

        // 保存支付订单信息
        payOrderService.saveOrder(PayOrderEntityLocalUtil.getPayOrderEntity(), PayOrderEntityLocalUtil.getPayOrderBodyEntity());

        return SResponse.ok(Boolean.TRUE);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SResponse<PayResultResp> queryPayResult(String payNo) {
        PayOrderEntity payOrder = payOrderService.getByPayNo(payNo);

        if (payOrder == null) {
            return SResponse.fail(Constant.ERRCODE_PARAM, "支付单不存在");

        } else if (payOrder.getPayStatus() == PayStatusConstant.PAY_SUCCESS) {
            return SResponse.ok(PayResultResp.createSuccess());
        } else if (payOrder.getPayStatus() == PayStatusConstant.PAY_Fail) {
            return SResponse.ok(PayResultResp.createFail("支付失败"));
        }
        PayOrderEntityLocalUtil.setPayOrder(payOrder, payOrderBodyService.getByPayNo(payNo));

        // 请求支付api 获取支付结果
        SResponse<PayResultResp> re = payServiceConfig
                .getPayService(EnumUtil.likeValueOf(PayTypeEnum.class, payOrder.getType()))
                .queryPayResult(payNo);

        // 更新支付单信息
        PayOrderEntityLocalUtil.setReqBody(null);
        PayOrderEntityLocalUtil.setRespBody(null);
        payOrderService.updateByPayQuery(payOrder, PayOrderEntityLocalUtil.getPayOrderBodyEntity());

        return re;
    }

    @Override
    public SResponse<WxWapPayResp> wxPayByJsap(PayOrderByWxJsapReq request) {
        // 1. 生产支付订单
        PayOrderEntityLocalUtil.initOrder(request.convertToPayOrderEntity());

        // 2. 向微信发送下单请求
        SResponse<WxWapPayResp> re = wxSpecialPayService.unifiedOrderByJsapi(request.getOpenid());

        // 保存支付订单信息
        payOrderService.saveOrder(PayOrderEntityLocalUtil.getPayOrderEntity(), PayOrderEntityLocalUtil.getPayOrderBodyEntity());

        return re;
    }

    @Override
    public SResponse<String> wxPayByMweb(PayOrderByWxMwebReq request) {
        // 1. 生产支付订单
        PayOrderEntityLocalUtil.initOrder(request.convertToPayOrderEntity());

        JSONObject sceneInfo = JSON.parseObject("{\"h5_info\": {\"type\":\"Wap\"," +
                "\"wap_url\": \"https://pay.shanzhimoo.com\",\"wap_name\": \"嘉鑫优选\"}}");

        // 2. 向微信发送下单请求
        SResponse<String> re = wxSpecialPayService.unifiedOrderByMweb(sceneInfo.toJSONString());

        // 保存支付订单信息
        payOrderService.saveOrder(PayOrderEntityLocalUtil.getPayOrderEntity(), PayOrderEntityLocalUtil.getPayOrderBodyEntity());

        return re;
    }

    @Override
    public String doWxNotify(String notifyData) throws Exception {
        // 回调返回微信信息
        String resXml;


        Map<String, String> notifyMap = SWxPayUtils.convertMapAndSignType(notifyData);

        MyWxPayConfig config = new MyWxPayConfig();
        WXPay wxpay = new WXPay(config);

        Boolean status = null;
        String payNo = notifyMap.get("out_trade_no");
        PayOrderEntity payOrderEntity = payOrderService.getByPayNo(payNo);

        if (payOrderEntity.getPayStatus() == PayStatusConstant.PAY_SUCCESS) {
            resXml = SWxPayUtils.getWxpayCallbackSuccessReturn();
        } else {
            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
                // 交易金额
                Integer totalFee = Integer.parseInt(notifyMap.get("total_fee"));

                if (payOrderEntity.getTotalFee().intValue() != totalFee.intValue()) {
                    log.error("微信支付回调失败!支付金额异常");

                    status = false;
                    resXml = SWxPayUtils.getWxpayCallbackFailReturn("支付金额异常");
                } else {

                    LocalDateTime finishTime = LocalDateTimeUtil.parse(notifyMap.get("time_end"), "yyyyMMddHHmmss");
                    SResponse<Boolean> re = payOrderService.updatePaySuccessStatus(payOrderEntity, finishTime);

                    if (re.getData()) {
                        status = true;
                        resXml = SWxPayUtils.getWxpayCallbackSuccessReturn();

                        // 更新门店订单支付信息
                        outletOrderManager.updatePayStatus(payOrderEntity.getOrderNo(), payOrderEntity.getPayNo());
                    } else {
                        log.error("微信支付回调处理失败! 业务逻辑处理异常");

                        status = false;
                        resXml = SWxPayUtils.getWxpayCallbackFailReturn("业务逻辑处理异常");
                    }
                }
            } else {
                log.error("微信支付回调失败!签名不一致");

                status = false;
                // 签名错误，如果数据里没有sign字段，也认为是签名错误
                resXml = SWxPayUtils.getWxpayCallbackFailReturn("签名不一致");
            }
        }

        // 保存回调结果
        payOrderCallbackService.saveCallbackInfo(payNo, status, notifyData);

        return resXml;
    }

    @Override
    public void doAliPayCallback(Map<String, String> map) {
        String payNo = map.get("trade_no");

        PayOrderEntity payOrder = payOrderService.getByPayNo(payNo);

        String tradeStatus = map.get("trade_status");

        /**
         * 交易状态：
         *      WAIT_BUYER_PAY（交易创建，等待买家付款）
         *      TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）
         *      TRADE_SUCCESS（交易支付成功）
         *      TRADE_FINISHED（交易结束，不可退款）
         */
        if ("TRADE_SUCCESS".equals(tradeStatus)) {
            payOrder.setPayStatus(PayStatusConstant.PAY_SUCCESS);

        } else if ("TRADE_CLOSED".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
            payOrder.setPayStatus(PayStatusConstant.PAY_Fail);

        }
        payOrder.setFinishTime(LocalDateTimeUtil.parse(map.get("gmt_close")));

        payOrderService.updateById(payOrder);
    }


    private PayOrderEntity producePayOrderInfo(PayOrderQrCodeReq request) {
        OutletOrderEntity outletOrder = outletOrderManager.obtainSelfOrderByNo(request.getOrderNo());

        List<OutletOrderDetailEntity> detailList = outletOrderDetailService.getDetailByOrderNo(outletOrder.getOrderNo());

        String feeType = request.getPayType() == PayTypeEnum.Wx ? "CNY" : "";
        return PayOrderEntity.builder()
                .type(request.getPayType().getCode())
                .payNo(BillNoUtils.generatePayNo())
                .orderNo(outletOrder.getOrderNo())
                .payStatus(PayStatusConstant.WAIT_PAY)
                .body("嘉鑫门店订单，" + detailList.get(0).getSkuName() + " " + detailList.get(0).getAttributeDesc())
                .feeType(feeType)
                .totalFee(MoneyUtils.changeToFen(outletOrder.getReceiveAmount()))
                .ip(NetUtil.getLocalhostStr())
                .tradeType(request.getPayType() == PayTypeEnum.Wx ? WxTradeTypeEnum.NATIVE.getDesc() : "")
                .productId(detailList.get(0).getSku())
                .build();

    }


}
